// goHupuWalker project utils.go
package main

import (
	"fmt"
	"os"
	"sort"
	"time"

	"github.com/PuerkitoBio/goquery"
	log "github.com/alecthomas/log4go"
	"github.com/go-gomail/gomail"
)

// get boards list from Hupu.com
// in: none
// out: boards list, err
func getBoardsList() ([]string, error) {
	doc, err := goquery.NewDocument("https://bbs.hupu.com/boards.php")
	if err != nil {
		log.Error("Cannot query boards from bbs.hupu.com/boards.php, %v", err)
		return nil, err
	}

	boardList := []string{}
	doc.Find("#basketball .plate_03_list a").Each(
		func(i int, contentSelection *goquery.Selection) {
			name := contentSelection.Text()
			url, exists := contentSelection.Attr("href")
			if exists {
				boardList = append(boardList, url)
				log.Info("Get board %d: %s -> %s", i, name, url)
			}
		})

	return boardList, nil
}

type NameCount struct {
	Name  string
	Count int
}

type NameCounts []NameCount

func (self NameCounts) Len() int {
	return len(self)
}

func (self NameCounts) Less(i, j int) bool {
	return self[i].Count > self[j].Count
}

func (self NameCounts) Swap(i, j int) {
	self[i], self[j] = self[j], self[i]
}

func sendResult(start time.Time, finish time.Time, result NameCounts, mailaddr string) {
	mailbody := fmt.Sprintf("<h2>Hupu 人名统计</h2>")
	mailbody += fmt.Sprintf("<p>开始时间：%s</p>", start.Format("2006-01-02 15:04:05"))
	mailbody += fmt.Sprintf("<p>结束时间：%s</p>", finish.Format("2006-01-02 15:04:05"))
	mailbody += fmt.Sprintf("<br>")
	mailbody += fmt.Sprintf("<p>统计信息（前20）：</p>")

	for i, nc := range result {
		if i >= 20 {
			break
		}
		mailbody += fmt.Sprintf("<p>%d: %s ... %d</p>", i+1, nc.Name, nc.Count)
	}

	gm := gomail.NewMessage()
	gm.SetHeader("From", "rw_testmail@163.com", "王岩")
	gm.SetHeader("To", gm.FormatAddress(mailaddr, "订阅人"))
	gm.SetHeader("Subject", "Hupu姓名统计上报_"+time.Now().Format("2006-01-02 15:04:05"))
	gm.SetBody("text/html", mailbody)

	dm := gomail.NewPlainDialer("smtp.163.com", 465, "rw_testmail@163.com", "830624wy")
	err := dm.DialAndSend(gm)
	if err != nil {
		log.Error("Send Mail failed: %v", err)
	}

}

var boardsFetchMap map[string]int = make(map[string]int)
var MAX_QUERY_WORKER int32 = 10
var lastFinishTime time.Time = time.Now()

func hupuWalk(init bool) {
	boardList, err := getBoardsList()
	if err != nil {
		log.Error("get board list failed, %v", err)
		return
	}

	topicTasks := CreateTaskPool(MAX_QUERY_WORKER, 4, 4)

	articleIds := []int{}
	nameCountMap := make(map[string]int)

	toFecthBoards := make(map[string]int)
	for _, url := range boardList {
		toFecthBoards[url] = boardsFetchMap[url]
	}

	go func() {
		for url, lastId := range toFecthBoards {
			topicTasks.InputChan <- CreateTopicList(url, lastId, func(out *TopicList) {
				boardsFetchMap[out.boardUrl] = out.lastTopicId
				articleIds = append(articleIds, out.topicList...)
			})
		}

		topicTasks.CloseInput()
	}()
	topicTasks.Join(nil)

	if init {
		lastFinishTime = time.Now()
		return
	}

	articleTasks := CreateTaskPool(MAX_QUERY_WORKER, 100, 4)
	go func() {
		for _, id := range articleIds {
			articleTasks.InputChan <- CreateTopicArticle(id, func(out *TopicArticle) {
				out.Merge(nameCountMap)
			})
		}

		articleTasks.CloseInput()
	}()
	articleTasks.Join(nil)

	finish := time.Now()

	sortingCount := make(NameCounts, len(nameCountMap))
	i := 0
	for name, count := range nameCountMap {
		sortingCount[i].Name = name
		sortingCount[i].Count = count
		i++
	}

	sort.Sort(sortingCount)

	for _, nameCount := range sortingCount {
		log.Info("Count: %s %d", nameCount.Name, nameCount.Count)
	}

	sendResult(lastFinishTime, finish, sortingCount, os.Args[1])
	lastFinishTime = finish
}
